package math;

import java.util.ArrayList;
import java.util.List;

public class LeastAbsMethodIterLS extends AbstractEstimateIterMin {
	
	public LeastAbsMethodIterLS (Double eps) {
		super(eps);
	}
	
	public LeastAbsMethodIterLS () {
		super(0.001);
	}
	
	public Double estimate (List<Double> V, List<Double> U) {
		//estimate b in V = b*U
		
		LeastSquareMethod lsm = new LeastSquareMethod();
		WLeastSquareMethod wlsm = new WLeastSquareMethod();
		Double coeff0 = lsm.estimate(V, U);
		Double coeff = coeff0;
			
		do {
			coeff0 = coeff;
			
			ArrayList<Double> W = new ArrayList<Double>();
			for (int i=0; i<U.size(); i++) {
				W.add(1.0/Math.abs(V.get(i)-coeff0*U.get(i)));
			}
			coeff = wlsm.estimate(V, U, W);
		} while (Math.abs(coeff-coeff0)>eps);
		
		return coeff;
	}
	
	public Double estimate1 (List<Double> V, List<Double> U) {
		Double eps = 0.0001;
		LeastSquareMethod lsm = new LeastSquareMethod();
		WLeastSquareMethod wlsm = new WLeastSquareMethod();
		Double coeff0 = lsm.estimate(V, U);
		Double coeff = coeff0;
			
		do {
			coeff0 = coeff;
			
			ArrayList<Double> W = new ArrayList<Double>();
			for (int i=0; i<U.size(); i++) {
				W.add(1.0/Math.abs(V.get(i)-coeff0*U.get(i)));
			}
			coeff = wlsm.estimate(V, U, W);
		} while (Math.abs(coeff-coeff0)>eps);
		
		return coeff;
	}
	
	public Vector2D estimateFull (List<Double> V, List<Double> U) {
		
		int n = U.size();
		ArrayList<Double> W = new ArrayList<Double>();
		for (int i = 0; i < n; i++) {
			W.add(1.0);
		}
		Vector2D coeff0 = calcWLSM(V, U, W);
		Vector2D coeff = coeff0;
		
		do {
			coeff0 = coeff;
			for (int i=0; i<U.size(); i++) {
				W.set(i,1.0/Math.abs(V.get(i)- coeff0.getV1()-coeff0.getV2()*U.get(i)));
			}
			coeff = calcWLSM(V, U, W);			
		} while (dist(coeff,coeff0)>eps);
		
		return coeff;
		
	}
	
	private Double dist (Vector2D a, Vector2D b) {
		return Math.sqrt(Math.pow(a.getV1()-b.getV1(), 2.0)+Math.pow(a.getV2()-b.getV2(), 2.0));
	}
	
	private Vector2D calcWLSM (List<Double> V, List<Double> U, List<Double> W) {
		//WLSM
		int n = U.size();
		Double sumUW = 0.0;
		Double sumVW = 0.0;
		Double sumW = 0.0;
		Double sumVUW = 0.0;
		Double sumU2W = 0.0;
		
		for (int i = 0; i < n; i++) {
			sumUW  += U.get(i)*W.get(i);
			sumVW  += V.get(i)*W.get(i);
			sumW   += W.get(i);
			sumVUW += V.get(i)*U.get(i)*W.get(i);
			sumU2W += U.get(i)*U.get(i)*W.get(i);
		}
		
		Double b1 = (sumVUW*sumW - sumVW*sumUW)/(sumU2W*sumW - sumUW*sumUW);
		Double b0 = (sumVW - b1*sumUW)/sumW;
		return new Vector2D(b0,b1);
	}

}
